package com.iwechat.adapter.mina;

import java.nio.charset.Charset;

import javax.annotation.PostConstruct;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.CumulativeProtocolDecoder;
import org.apache.mina.filter.codec.ProtocolDecoderOutput;
import org.apache.mina.proxy.utils.ByteUtilities;

import com.iwechat.adapter.mina.entity.Active;
import com.iwechat.adapter.mina.entity.ActiveResp;
import com.iwechat.adapter.mina.entity.Connect;
import com.iwechat.adapter.mina.entity.ConnectResp;
import com.iwechat.adapter.mina.entity.Exit;
import com.iwechat.adapter.mina.entity.ExitResp;
import com.iwechat.adapter.mina.entity.PDU;
import com.iwechat.adapter.mina.entity.PDUHeader;
import com.iwechat.adapter.mina.entity.Submit;
import com.iwechat.adapter.mina.entity.SubmitResp;

public class NioProtocolDecoder extends CumulativeProtocolDecoder {
    private static final Logger logger = LogManager.getLogger();

    private String              charset;
    private String              preFix;
    private Charset             charSet;

    @PostConstruct
    public void initCoder() {
        this.charSet = Charset.forName(charset);
        logger.info("### " + this + " init succeed!");
    }

    public String getCharset() {
        return charset;
    }

    public String getPreFix() {
        return preFix;
    }

    public void setCharset(String charset) {
        this.charset = charset;
    }

    public void setPreFix(String prefix) {
        this.preFix = prefix;
    }

    @Override
    protected boolean doDecode(IoSession ioSession, IoBuffer in, ProtocolDecoderOutput out) throws Exception {
        int preLen = preFix.length();

        if (in.remaining() >= preLen + PDU.HEAD_LEN) {

            in.mark();

            byte[] buff;
            String thisPreFix;
            buff = new byte[preLen];
            in.get(buff);
            thisPreFix = new String(buff, charSet);
            if (!preFix.equals(thisPreFix)) {
                // 数据错位,断开连接
                String hxstr1 = ByteUtilities.asHex(preFix.getBytes(charSet));
                String hxstr2 = ByteUtilities.asHex(buff);
                ioSession.closeNow();
                logger.error("sessionId[" + ioSession.getId() + "]检查到前导符不正确,应为[" + hxstr1 + "],实际为[" + hxstr2 + "],可能是数据错位，断开链接！");
                logger.error("sessionId[" + ioSession.getId() + "]" + ",HexDump: " + ByteUtilities.asHex(in.array()));
                return false;
            }
            int pkgLen = in.getInt();
            if (in.remaining() >= pkgLen - 4) {
                // 解析数据
                int command = in.getInt();
                int sequence = in.getInt();
                long timestamp = in.getLong();

                PDUHeader header = new PDUHeader(false);
                header.setPkgLen(pkgLen);
                header.setCommand(command);
                header.setSequence(sequence);
                header.setTimestamp(timestamp);

                switch (command) {
                case PDU.COMMAND_ACTIVE: {
                    out.write(new Active(header));
                }
                break;
                case PDU.COMMAND_ACTIVE_RESP: {
                    out.write(new ActiveResp(header));
                }
                break;
                case PDU.COMMAND_CONNECT: {
                    out.write(new Connect(header, in, charSet));
                }
                break;
                case PDU.COMMAND_EXIT: {
                    out.write(new Exit(header));
                }
                break;
                case PDU.COMMAND_EXIT_RESP: {
                    out.write(new ExitResp(header));
                }
                break;
                case PDU.COMMAND_SUBMIT: {
                    out.write(new Submit(header, in));
                }
                break;
                case PDU.COMMAND_SUBMIT_RESP: {
                    out.write(new SubmitResp(header, in));
                }
                break;
                case PDU.COMMAND_CONNECT_RESP: {
                    out.write(new ConnectResp(header, in, charSet));
                }
                break;
                case PDU.COMMAND_DELIVEY: {
                    // 暂无
                }
                break;
                case PDU.COMMAND_DELIVEY_RESP: {
                    // 暂无
                }
                break;
                default: {
                    logger.error("sessionId[" + ioSession.getId() + "]报文头中command=" + command + ",不合法,消息已丢弃,并断开链接!");
                    logger.error("sessionId[" + ioSession.getId() + "]" + ",HexDump: " + ByteUtilities.asHex(in.array()));
                    ioSession.closeNow();
                    return false;
                }
                }

                if (in.remaining() >= preLen + PDU.HEAD_LEN) {
                    // 剩余消息够一个报文头
                    return true;
                }
            } else {
                // 剩余数据不足一个PDU
                in.reset();
            }
        }
        // else { // 不足一个报文头 }
        return false;
    }

    @PostConstruct
    void init() {

    }

}
